From 0ea4d2fb34ca99021271c8c16e8b3d87af3c5611 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Wed, 5 Dec 2007 09:44:20 +0000 Subject: [PATCH] Implement legacy XML-RPC interface for ACM commands. This patch implements a (non Xen-API) legacy XML-RPC interface for the ACM commands and funnels the calls into code introduced by the Xen-API support for ACM security management. Since some of the functionality has changed, also the xm applications have changed. In particular the following old commands have been removed along with some tools the have become obsolete now: - loadpolicy (included in: setpolicy) - makepolicy (included in: setpolicy) - cfgbootpolicy (included in: setpolicy) and the following commands been introduced: - setpolicy - getpolicy - resetpolicy All tools have been adapted to work in Xen-API and legacy XML-RPC mode. Both modes support the same functionality. Signed-off-by: Stefan Berger --- docs/man/xm.pod.1 | 61 +- tools/python/xen/util/acmpolicy.py | 30 +- tools/python/xen/util/xsm/acm/acm.py | 319 +++- tools/python/xen/util/xsm/dummy/dummy.py | 66 +- tools/python/xen/util/xsm/flask/flask.py | 3 + tools/python/xen/xend/XendConfig.py | 23 +- tools/python/xen/xend/XendDomainInfo.py | 16 +- tools/python/xen/xend/XendXSPolicyAdmin.py | 12 +- tools/python/xen/xend/server/XMLRPCServer.py | 6 + tools/python/xen/xm/activatepolicy.py | 111 -- tools/python/xen/xm/addlabel.py | 112 +- tools/python/xen/xm/cfgbootpolicy.py | 227 --- tools/python/xen/xm/create.py | 124 +- tools/python/xen/xm/dry-run.py | 107 +- tools/python/xen/xm/getlabel.py | 62 +- tools/python/xen/xm/getpolicy.py | 101 +- tools/python/xen/xm/labels.py | 32 +- tools/python/xen/xm/loadpolicy.py | 70 - tools/python/xen/xm/main.py | 33 +- tools/python/xen/xm/makepolicy.py | 51 - tools/python/xen/xm/resetpolicy.py | 162 ++ tools/python/xen/xm/resources.py | 8 +- tools/python/xen/xm/rmlabel.py | 94 +- tools/python/xen/xm/setpolicy.py | 180 +- tools/security/Makefile | 9 +- .../example/client_v1-security_policy.xml | 4 +- .../example/ste/client_v1-security_policy.xml | 149 -- tools/security/secpol_xml2bin.c | 1457 ----------------- tools/security/secpol_xml2bin.h | 166 -- tools/xm-test/lib/XmTestLib/acm.py | 58 +- .../security-acm/01_security-acm_basic.py | 6 - tools/xm-test/tests/security-acm/acm_utils.py | 2 +- 32 files changed, 1001 insertions(+), 2860 deletions(-) delete mode 100644 tools/python/xen/xm/activatepolicy.py delete mode 100644 tools/python/xen/xm/cfgbootpolicy.py delete mode 100644 tools/python/xen/xm/loadpolicy.py delete mode 100644 tools/python/xen/xm/makepolicy.py create mode 100644 tools/python/xen/xm/resetpolicy.py delete mode 100644 tools/security/policies/example/ste/client_v1-security_policy.xml delete mode 100644 tools/security/secpol_xml2bin.c delete mode 100644 tools/security/secpol_xml2bin.h diff --git a/docs/man/xm.pod.1 b/docs/man/xm.pod.1 index 72932dee93..3e98fadc9c 100644 --- a/docs/man/xm.pod.1 +++ b/docs/man/xm.pod.1 @@ -821,15 +821,13 @@ security in Xen, you must compile Xen with ACM support enabled as described under "Configuring Security" below. There, you will find also examples of each subcommand described here. -=item B ACM I I<[--load|--boot]> +=item B ACM I Makes the given ACM policy available to xend as a I. The policy is compiled and a mapping (.map) as well as a binary (.bin) -version of the policy is created. If the option I<--load> is provided -the policy is loaded into Xen. If the option I<--boot> is provided the -system is configure to be loaded with the policy at boot time. If these -options are not provided with the B subcommand, the -B subcommand provides this functionality. +version of the policy is created. The policy is loaded and the system's +bootloader is prepared to boot the system with this policy the next time +it is started. =over 4 @@ -844,16 +842,13 @@ global policy root directory. =back -=item B I<[--load|--boot]> +=item B -Activates the xend-managed policy by loading it into Xen using the -I<--load> option or configures the system to boot with the -xend-managed policy during the next reboot as a result of the -I<--boot> option. The latter is only supported if the system is booted -with the grub boot loader and the default boot title is modified. -It copies the binary policy representation into the /boot directory and -adds a module line specifying the binary policy to the /boot/grub/menu.lst -or /boot/grub/grub.conf file. +Reset the system's policy to the default state where the DEFAULT policy +is loaded and enforced. This operation may fail if for example guest VMs are +running and and one of them uses a different label than what Domain-0 +does. It is best to make sure that no guests are running before issuing +this command. =item B [--dumpxml] @@ -938,47 +933,39 @@ B In xen_source_dir/Config.mk set the following parameter: + XSM_ENABLE ?= y ACM_SECURITY ?= y + Then recompile and install xen and the security tools and then reboot: - cd xen_source_dir/xen; make clean; make; cp xen.gz /boot; - cd xen_source_dir/tools/security; make install; + cd xen_source_dir; make clean; make install reboot into Xen =back -B +B =over 4 -This step makes the policy available to xend and creates the client_v1.map and -client_v1.bin files in /etc/xen/acm-security/policies/example/chwall_ste. - - xm setpolicy ACM example.client_v1 +To set the system's security policy enforcement into its default state, +the follow command can be issued. Make sure that no guests are running +while doing this. -=back + xm resetpolicy -B - -=over 4 - -This step activates the xend-manged policy as new security policy in Xen. -You can use the dumppolicy subcommand before and afterwards to see the -change in the Xen policy state. - - xm activatpolicy --load +After this command has successfully completed, the system's DEFAULT policy +is enforced. =back -B +B =over 4 -This configures the boot loader to load the current xend-managed policy at -boot time. During system start, the ACM configures Xen with this policy and -Xen enforces this policy from then on. +This step sets the system's policy and automatically loads it into Xen +for enforcement. - xm activatepolicy --boot + xm setpolicy ACM example.client_v1 =back diff --git a/tools/python/xen/util/acmpolicy.py b/tools/python/xen/util/acmpolicy.py index 3f247d2a52..13bcda377a 100644 --- a/tools/python/xen/util/acmpolicy.py +++ b/tools/python/xen/util/acmpolicy.py @@ -51,6 +51,19 @@ ACM_SCHEMA_FILE = ACM_POLICIES_DIR + "security_policy.xsd" ACM_LABEL_UNLABELED = "__UNLABELED__" ACM_LABEL_UNLABELED_DISPLAY = "unlabeled" +""" + Error codes reported in when trying to test for a new policy + These error codes are reported in an array of tuples where + each error code is followed by a parameter describing the error + more closely, such as a domain id. +""" +ACM_EVTCHN_SHARING_VIOLATION = 0x100 +ACM_GNTTAB_SHARING_VIOLATION = 0x101 +ACM_DOMAIN_LOOKUP = 0x102 +ACM_CHWALL_CONFLICT = 0x103 +ACM_SSIDREF_IN_USE = 0x104 + + class ACMPolicy(XSPolicy): """ ACMPolicy class. Implements methods for getting information from @@ -228,7 +241,7 @@ class ACMPolicy(XSPolicy): return -xsconstants.XSERR_BAD_LABEL, errors #Get binary and map from the new policy - rc, map, bin_pol = acmpol_new.policy_create_map_and_bin() + rc, pol_map, bin_pol = acmpol_new.policy_create_map_and_bin() if rc != xsconstants.XSERR_SUCCESS: log.error("Could not build the map and binary policy.") return rc, errors @@ -356,7 +369,7 @@ class ACMPolicy(XSPolicy): pass return ssidref - def set_vm_bootlabel(self, vm_label): + def set_vm_bootlabel(self, vm_label, remove=False): parms="<>" if vm_label != "": ssidref = self.vmlabel_to_ssidref(vm_label) @@ -367,6 +380,10 @@ class ACMPolicy(XSPolicy): self.get_name(),vm_label) else: ssidref = 0 #Identifier for removal + + if remove == True: + parms = "<>" + try: def_title = bootloader.get_default_title() bootloader.set_kernel_attval(def_title, "ssidref", parms) @@ -387,7 +404,7 @@ class ACMPolicy(XSPolicy): if name: p = name.split(".") path = "" - if dotted == True: + if dotted: sep = "." else: sep = "/" @@ -513,8 +530,8 @@ class ACMPolicy(XSPolicy): self.set_frompolicy_name(curpol.policy_dom_get_hdr_item("PolicyName")) version = curpol.policy_dom_get_hdr_item("Version") self.set_frompolicy_version(version) - (maj, min) = self.__convVersionToTuple(version) - self.set_policy_version("%s.%s" % (maj, min+1)) + (maj, minor) = self.__convVersionToTuple(version) + self.set_policy_version("%s.%s" % (maj, minor+1)) # # Get all types that are part of a node @@ -877,8 +894,7 @@ class ACMPolicy(XSPolicy): """ Determine whether this policy is the active one. """ - security.refresh_security_policy() - if self.get_name() == security.active_policy: + if self.get_name() == security.get_active_policy_name(): return True return False diff --git a/tools/python/xen/util/xsm/acm/acm.py b/tools/python/xen/util/xsm/acm/acm.py index 47e9ce586d..d8f4be4314 100644 --- a/tools/python/xen/util/xsm/acm/acm.py +++ b/tools/python/xen/util/xsm/acm/acm.py @@ -24,6 +24,7 @@ import os, string, re import threading import struct import stat +import base64 from xen.lowlevel import acm from xen.xend import sxp from xen.xend import XendConstants @@ -39,7 +40,6 @@ policy_dir_prefix = security_dir_prefix + "/policies" res_label_filename = policy_dir_prefix + "/resource_labels" boot_filename = "/boot/grub/menu.lst" altboot_filename = "/boot/grub/grub.conf" -xensec_xml2bin = "/usr/sbin/xensec_xml2bin" xensec_tool = "/usr/sbin/xensec_tool" #global patterns for map file @@ -49,7 +49,7 @@ secondary_entry_re = re.compile("\s*SECONDARY\s+.*", re.IGNORECASE) label_template_re = re.compile(".*security_label_template.xml", re.IGNORECASE) mapping_filename_re = re.compile(".*\.map", re.IGNORECASE) policy_reference_entry_re = re.compile("\s*POLICYREFERENCENAME\s+.*", re.IGNORECASE) -vm_label_re = re.compile("\s*LABEL->SSID\s+VM\s+.*", re.IGNORECASE) +vm_label_re = re.compile("\s*LABEL->SSID\s.+[VM|ANY]\s+.*", re.IGNORECASE) res_label_re = re.compile("\s*LABEL->SSID\s+RES\s+.*", re.IGNORECASE) all_label_re = re.compile("\s*LABEL->SSID\s+.*", re.IGNORECASE) access_control_re = re.compile("\s*access_control\s*=", re.IGNORECASE) @@ -77,9 +77,25 @@ __resfile_lock = threading.RLock() log = logging.getLogger("xend.util.security") + +#Functions exported through XML-RPC +xmlrpc_exports = [ + 'set_resource_label', + 'get_resource_label', + 'list_labels', + 'get_labeled_resources', + 'set_policy', + 'get_policy', + 'activate_policy', + 'rm_bootpolicy', + 'get_xstype', + 'get_domain_label', + 'set_domain_label' +] + # Our own exception definition. It is masked (pass) if raised and # whoever raises this exception must provide error information. -class ACMError(Exception): +class XSMError(Exception): def __init__(self,value): self.value = value def __str__(self): @@ -90,7 +106,7 @@ class ACMError(Exception): def err(msg): """Raise ACM exception. """ - raise ACMError(msg) + raise XSMError(msg) @@ -118,12 +134,17 @@ def refresh_security_policy(): global active_policy active_policy = 'INACCESSIBLE' + if os.access("/proc/xen/privcmd", os.R_OK|os.W_OK): try: active_policy = acm.policy() except: active_policy = "INACTIVE" +def get_active_policy_name(): + refresh_security_policy() + return active_policy + # now set active_policy refresh_security_policy() @@ -132,8 +153,7 @@ def on(): returns none if security policy is off (not compiled), any string otherwise, use it: if not security.on() ... """ - refresh_security_policy() - return (active_policy not in ['INACTIVE', 'NULL']) + return (get_active_policy_name() not in ['INACTIVE', 'NULL']) def calc_dom_ssidref_from_info(info): @@ -158,11 +178,10 @@ def calc_dom_ssidref_from_info(info): typ, policyname, vmlabel = seclab.split(":") if typ != xsconstants.ACM_POLICY_ID: raise VmError("Policy type '%s' must be changed." % typ) - refresh_security_policy() - if active_policy != policyname: + if get_active_policy_name() != policyname: raise VmError("Active policy '%s' different than " "what in VM's label ('%s')." % - (active_policy, policyname)) + (get_active_policy_name(), policyname)) ssidref = label2ssidref(vmlabel, policyname, "dom") return ssidref else: @@ -180,7 +199,7 @@ def getmapfile(policyname): 4. True if policy file is available, False otherwise """ if not policyname: - policyname = active_policy + policyname = get_active_policy_name() map_file_ok = False primary = None secondary = None @@ -199,8 +218,7 @@ def getmapfile(policyname): if not os.path.isfile(policy_filename): err("Policy file \'" + policy_filename + "\' not found.") else: - err("Mapping file \'" + map_filename + "\' not found." + - " Use xm makepolicy to create it.") + err("Mapping file \'" + map_filename + "\' not found.") f = open(map_filename) for line in f: @@ -221,7 +239,7 @@ def getmapfile(policyname): if map_file_ok and primary and secondary: return (primary, secondary, f, True) else: - err("Mapping file inconsistencies found. Try makepolicy to create a new one.") + err("Mapping file inconsistencies found.") @@ -253,10 +271,10 @@ def ssidref2label(ssidref_var): (primary, secondary, f, pol_exists) = getmapfile(None) if not f: if (pol_exists): - err("Mapping file for policy not found.\n" + - "Please use makepolicy command to create mapping file!") + err("Mapping file for policy not found.") else: - err("Policy file for \'" + active_policy + "\' not found.") + err("Policy file for \'" + get_active_policy_name() + + "\' not found.") #2. get labelnames for both ssidref parts pri_ssid = ssidref & 0xffff @@ -534,37 +552,99 @@ def hv_get_policy(): return rc, bin_pol -def make_policy(policy_name): - policy_file = string.join(string.split(policy_name, "."), "/") - if not os.path.isfile(policy_dir_prefix + "/" + policy_file + "-security_policy.xml"): - err("Unknown policy \'" + policy_name + "\'") +def set_policy(xs_type, xml, flags, overwrite): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + xspoladmin = XendXSPolicyAdmin.XSPolicyAdminInstance() + try: + acmpol, rc, errors = \ + xspoladmin.add_acmpolicy_to_system(xml, + int(flags), + True) + return rc, base64.b64encode(errors) + except Exception, e: + err(str(e)) - (ret, output) = commands.getstatusoutput(xensec_xml2bin + " -d " + policy_dir_prefix + " " + policy_file) - if ret: - err("Creating policy failed:\n" + output) -def load_policy(policy_name): - global active_policy - policy_file = policy_dir_prefix + "/" + string.join(string.split(policy_name, "."), "/") - if not os.path.isfile(policy_file + ".bin"): - if os.path.isfile(policy_file + "-security_policy.xml"): - err("Binary file does not exist." + - "Please use makepolicy to build the policy binary.") - else: - err("Unknown Policy " + policy_name) +def get_policy(): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + poladmin = XendXSPolicyAdmin.XSPolicyAdminInstance() + try: + policy = poladmin.get_loaded_policy() + if policy != None: + return policy.toxml(), poladmin.get_policy_flags(policy) + except Exception, e: + err(str(e)) + return "", 0 - #require this policy to be the first or the same as installed - if active_policy not in ['DEFAULT', policy_name]: - err("Active policy \'" + active_policy + - "\' incompatible with new policy \'" + policy_name + "\'") - (ret, output) = commands.getstatusoutput(xensec_tool + " loadpolicy " + policy_file + ".bin") - if ret: - err("Loading policy failed:\n" + output) +def activate_policy(flags): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + poladmin = XendXSPolicyAdmin.XSPolicyAdminInstance() + try: + policies = poladmin.get_policies() + if len(policies) > 0: + flags = int(flags) + irc = poladmin.activate_xspolicy(policies[0], flags) + return irc + except Exception, e: + err("Error while activating the policy: " % str(e)) + return 0 + + +def rm_bootpolicy(): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + rc = XendXSPolicyAdmin.XSPolicyAdminInstance().rm_bootpolicy() + if rc != xsconstants.XSERR_SUCCESS: + err("Error while removing boot policy: %s" % \ + str(xsconstants.xserr2string(-rc))) + return rc + + +def get_xstype(): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendXSPolicyAdmin + return XendXSPolicyAdmin.XSPolicyAdminInstance().isXSEnabled() + + +def get_domain_label(domain): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendDomain + dom = XendDomain.instance().domain_lookup_nr(domain) + if dom: + seclab = dom.get_security_label() + return seclab else: - # refresh active policy - refresh_security_policy() + err("Domain not found.") +def set_domain_label(domain, seclab, old_seclab): + """ + Xend exports this function via XML-RPC + """ + from xen.xend import XendDomain + dom = XendDomain.instance().domain_lookup_nr(domain) + if dom: + results = dom.set_security_label(seclab, old_seclab) + rc, errors, old_label, new_ssidref = results + return rc, new_ssidref + else: + err("Domain not found.") + def dump_policy(): if active_policy in ['NULL', 'INACTIVE', 'INACCESSIBLE' ]: @@ -589,16 +669,32 @@ def dump_policy_file(filename, ssidref=None): print output -def list_labels(policy_name, condition): - if (not policy_name) and active_policy in \ - [ 'NULL', 'INACTIVE', 'DEFAULT', 'INACCESSIBLE' ]: - err("Current policy \'" + active_policy + "\' has no labels defined.\n") +def list_labels(policy_name, ltype): + """ + Xend exports this function via XML-RPC + + List the VM,resource or any kind of labels contained in the + given policy. If no policy name is given, the currently + active policy's label will be returned if they exist. + """ + if not policy_name: + if active_policy in [ 'NULL', 'INACTIVE', "" ]: + err("Current policy \'" + active_policy + "\' " + "has no labels defined.\n") + + if not ltype or ltype == 'dom': + condition = vm_label_re + elif ltype == 'res': + condition = res_label_re + elif ltype == 'any': + condition = all_label_re + else: + err("Unknown label type \'" + ltype + "\'") (primary, secondary, f, pol_exists) = getmapfile(policy_name) if not f: if pol_exists: - err("Cannot find mapfile for policy \'" + policy_name + - "\'.\nPlease use makepolicy to create mapping file.") + err("Cannot find mapfile for policy \'" + policy_name + "\'.\n") else: err("Unknown policy \'" + policy_name + "\'") @@ -608,6 +704,10 @@ def list_labels(policy_name, condition): label = line.split()[3] if label not in labels: labels.append(label) + + if '__NULL_LABEL__' in labels: + labels.remove('__NULL_LABEL__') + return labels @@ -763,10 +863,10 @@ def res_security_check(resource, domain_label): # provide descriptive error messages if decision == 'DENIED': if label == ssidref2label(NULL_SSIDREF): - raise ACMError("Resource '"+resource+"' is not labeled") + raise XSMError("Resource '"+resource+"' is not labeled") rtnval = 0 else: - raise ACMError("Permission denied for resource '"+resource+"' because label '"+label+"' is not allowed") + raise XSMError("Permission denied for resource '"+resource+"' because label '"+label+"' is not allowed") rtnval = 0 # security is off, make sure resource isn't labeled @@ -775,7 +875,7 @@ def res_security_check(resource, domain_label): # xm without ACM are free to use relative paths. (policytype, label, policy) = get_res_label(resource) if policy != 'NULL': - raise ACMError("Security is off, but '"+resource+"' is labeled") + raise XSMError("Security is off, but '"+resource+"' is labeled") rtnval = 0 return rtnval @@ -803,10 +903,10 @@ def res_security_check_xapi(rlabel, rssidref, rpolicy, xapi_dom_label): # provide descriptive error messages if decision == 'DENIED': if rlabel == ssidref2label(NULL_SSIDREF): - #raise ACMError("Resource is not labeled") + #raise XSMError("Resource is not labeled") rtnval = 0 else: - #raise ACMError("Permission denied for resource because label '"+rlabel+"' is not allowed") + #raise XSMError("Permission denied for resource because label '"+rlabel+"' is not allowed") rtnval = 0 # security is off, make sure resource isn't labeled @@ -814,17 +914,35 @@ def res_security_check_xapi(rlabel, rssidref, rpolicy, xapi_dom_label): # Note, we can't canonicalise the resource here, because people using # xm without ACM are free to use relative paths. if rpolicy != 'NULL': - #raise ACMError("Security is off, but resource is labeled") + #raise XSMError("Security is off, but resource is labeled") rtnval = 0 return rtnval -def validate_label(label, policyref): +def validate_label_xapi(xapi_label, dom_or_res): + """ + Make sure that this label is part of the currently enforced policy + and that it references the current policy. + dom_or_res defines whether this is a VM ('res') or resource label + ('res') + """ + tmp = xapi_label.split(":") + if len(tmp) != 3: + return -xsconstants.XSERR_BAD_LABEL_FORMAT + policytyp, policyref, label = tmp + return validate_label(policytyp, policyref, label, dom_or_res) + + +def validate_label(policytype, policyref, label, dom_or_res): """ Make sure that this label is part of the currently enforced policy and that it reference the current policy. """ + if policytype != xsconstants.ACM_POLICY_ID: + return -xsconstants.XSERR_WRONG_POLICY_TYPE + if not policytype or not label: + return -xsconstants.XSERR_BAD_LABEL_FORMAT rc = xsconstants.XSERR_SUCCESS from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance curpol = XSPolicyAdminInstance().get_loaded_policy() @@ -832,7 +950,7 @@ def validate_label(label, policyref): rc = -xsconstants.XSERR_BAD_LABEL else: try: - label2ssidref(label, curpol.get_name() , 'res') + label2ssidref(label, curpol.get_name() , dom_or_res) except: rc = -xsconstants.XSERR_BAD_LABEL return rc @@ -851,11 +969,11 @@ def set_resource_label_xapi(resource, reslabel_xapi, oldlabel_xapi): olabel = "" if reslabel_xapi == "": return rm_resource_label(resource, oldlabel_xapi) - typ, policyref, label = reslabel_xapi.split(":") - if typ != xsconstants.ACM_POLICY_ID: - return -xsconstants.XSERR_WRONG_POLICY_TYPE - if not policyref or not label: - return -xsconstants.XSERR_BAD_LABEL_FORMAT + + rc = validate_label_xapi(reslabel_xapi, 'res') + if rc != xsconstants.XSERR_SUCCESS: + return rc + if oldlabel_xapi not in [ "" ]: tmp = oldlabel_xapi.split(":") if len(tmp) != 3: @@ -866,9 +984,7 @@ def set_resource_label_xapi(resource, reslabel_xapi, oldlabel_xapi): otyp != xsconstants.INVALID_POLICY_PREFIX + \ xsconstants.ACM_POLICY_ID: return -xsconstants.XSERR_WRONG_POLICY_TYPE - rc = validate_label(label, policyref) - if rc != xsconstants.XSERR_SUCCESS: - return rc + typ, policyref, label = reslabel_xapi.split(":") return set_resource_label(resource, typ, policyref, label, olabel) @@ -1033,7 +1149,10 @@ def __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel, def set_resource_label(resource, policytype, policyref, reslabel, \ oreslabel = None): - """Assign a label to a resource + """ + Xend exports this function via XML-RPC. + + Assign a label to a resource If the old label (oreslabel) is given, then the resource must have that old label. A resource label may be changed if @@ -1046,6 +1165,10 @@ def set_resource_label(resource, policytype, policyref, reslabel, \ @rtype: int @return Success (0) or failure value (< 0) """ + + if reslabel != "": + ssidref = label2ssidref(reslabel, policyref, 'res') + try: resource = unify_resname(resource, mustexist=False) except Exception: @@ -1123,7 +1246,10 @@ def format_resource_label(res): return "" def get_resource_label(resource): - """Get the assigned resource label of a given resource + """ + Xend exports this function via XML-RPC. + + Get the assigned resource label of a given resource @param resource: The name of a resource, i.e., "phy:/dev/hda" @rtype: list @@ -1161,7 +1287,10 @@ def get_labeled_resources_xapi(): def get_labeled_resources(): - """Get a map of all labeled resources + """ + Xend exports this function via XML-RPC + + Get a map of all labeled resources. @rtype: list @return list of labeled resources """ @@ -1225,6 +1354,7 @@ def change_acm_policy(bin_pol, del_array, chg_array, This function should be called with the lock to the domains held (XendDomain.instance().domains_lock) """ + from xen.util.acmpolicy import ACM_LABEL_UNLABELED rc = xsconstants.XSERR_SUCCESS domain_label_map = {} @@ -1266,14 +1396,25 @@ def change_acm_policy(bin_pol, del_array, chg_array, continue # label been renamed or deleted? - if reslabel_map.has_key(label) and cur_policyname == policy: + if policytype != xsconstants.ACM_POLICY_ID: + continue + elif reslabel_map.has_key(label) and cur_policyname == policy: + # renaming of an active label; policy may have been renamed label = reslabel_map[label] + polname = new_policyname elif label not in polnew_reslabels: + # label been removed policytype = xsconstants.INVALID_POLICY_PREFIX + policytype run_resource_label_change_script(key, "", "remove") + polname = policy + else: + # no change to label + policytype = xsconstants.ACM_POLICY_ID + polname = new_policyname + # Update entry access_control[key] = \ - tuple([ policytype, new_policyname, label ]) + tuple([ policytype, polname, label ]) # All resources have new labels in the access_control map # There may still be labels in there that are invalid now. @@ -1297,11 +1438,19 @@ def change_acm_policy(bin_pol, del_array, chg_array, new_vmlabel = vmlabel if vmlabel_map.has_key(vmlabel): + # renaming of the label new_vmlabel = vmlabel_map[vmlabel] - if new_vmlabel not in polnew_vmlabels: + polname = new_policyname + elif new_vmlabel not in polnew_vmlabels and \ + vmlabel != ACM_LABEL_UNLABELED: + # removal of VM label and not the 'unlabeled' label policytype = xsconstants.INVALID_POLICY_PREFIX + policytype + polname = policy + else: + polname = new_policyname + new_seclab = "%s:%s:%s" % \ - (policytype, new_policyname, new_vmlabel) + (policytype, polname, new_vmlabel) domain_label_map[dominfo] = [ sec_lab, new_seclab ] @@ -1383,16 +1532,20 @@ def get_security_label(self, xspol=None): return label def run_resource_label_change_script(resource, label, command): - script = XendOptions.instance().get_resource_label_change_script() - if script: - parms = { - 'resource' : resource, - 'label' : label, - 'command' : command, - } - log.info("Running resource label change script %s: %s" % - (script, parms)) - parms.update(os.environ) - os.spawnve(os.P_NOWAIT, script[0], script, parms) - else: - log.info("No script given for relabeling of resources.") + def __run_resource_label_change_script(label, command): + script = XendOptions.instance().get_resource_label_change_script() + if script: + parms = { + 'resource' : resource, + 'label' : label, + 'command' : command, + } + log.info("Running resource label change script %s: %s" % + (script, parms)) + parms.update(os.environ) + os.spawnve(os.P_WAIT, script[0], script, parms) + else: + log.info("No script given for relabeling of resources.") + thread = threading.Thread(target=__run_resource_label_change_script, + args=(label,command)) + thread.start() diff --git a/tools/python/xen/util/xsm/dummy/dummy.py b/tools/python/xen/util/xsm/dummy/dummy.py index 42a41b36c9..7a07e906d0 100644 --- a/tools/python/xen/util/xsm/dummy/dummy.py +++ b/tools/python/xen/util/xsm/dummy/dummy.py @@ -1,4 +1,6 @@ import sys +from xen.util import xsconstants +from xen.xend.XendLogging import log class XSMError(Exception): def __init__(self,value): @@ -6,11 +8,27 @@ class XSMError(Exception): def __str__(self): return repr(self.value) + security_dir_prefix = ""; policy_dir_prefix = ""; active_policy = ""; NULL_SSIDREF = 0; +#Functions exported through XML-RPC +xmlrpc_exports = [ + 'set_resource_label', + 'get_resource_label', + 'list_labels', + 'get_labeled_resources', + 'set_policy', + 'get_policy', + 'activate_policy', + 'rm_bootpolicy', + 'get_xstype', + 'get_domain_label', + 'set_domain_label' +] + def err(msg): """Raise XSM-dummy exception. """ @@ -45,7 +63,7 @@ def calc_dom_ssidref_from_info(info): return "" def set_security_label(policy, label): - return "" + return "" def ssidref2security_label(ssidref): return "" @@ -55,3 +73,49 @@ def has_authorization(ssidref): def get_security_label(self, xspol=None): return "" + +def get_resource_label_xapi(resource): + return "" + +def get_labeled_resources_xapi(): + return {} + +def set_resource_label_xapi(resource, reslabel_xapi, oldlabel_xapi): + err("Command not supported under XSM 'dummy' module.") + +def format_resource_label(res): + return "" + +def set_resource_label(resource, policytype, policyref, reslabel, + oreslabel = None): + err("Command not supported under XSM 'dummy' module.") + +def get_resource_label(resource): + return "" + +def list_labels(policy_name, ltype): + return [] + +def get_labeled_resources(): + return {} + +def set_policy(xs_type, xml, flags, overwrite): + err("Command not supported under xsm 'dummy' module.") + +def get_policy(): + return "", 0 + +def activate_policy(): + err("Command not supported under xsm 'dummy' module.") + +def rm_bootpolicy(): + err("Command not supported under xsm 'dummy' module.") + +def get_xstype(): + return 0 + +def get_domain_label(domain): + return "" + +def set_domain_label(): + err("Command not supported under xsm 'dummy' module.") diff --git a/tools/python/xen/util/xsm/flask/flask.py b/tools/python/xen/util/xsm/flask/flask.py index a0f931b364..933f0898c8 100644 --- a/tools/python/xen/util/xsm/flask/flask.py +++ b/tools/python/xen/util/xsm/flask/flask.py @@ -2,6 +2,9 @@ import sys from xen.lowlevel import flask from xen.xend import sxp +#Functions exported through XML-RPC +xmlrpc_exports = [ ] + def err(msg): """Raise XSM-Flask exception. """ diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py index d43355d2ba..bdd8e6dff6 100644 --- a/tools/python/xen/xend/XendConfig.py +++ b/tools/python/xen/xend/XendConfig.py @@ -647,11 +647,18 @@ class XendConfig(dict): except ValueError, e: raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e)) - if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'): - cfg['security'] = sxp.child_value(sxp_cfg, 'security') - if 'security' in cfg and not cfg.get('security_label'): - secinfo = cfg['security'] - if isinstance(secinfo, list): + import xen.util.xsm.xsm as security + if security.on(): + from xen.util.acmpolicy import ACM_LABEL_UNLABELED + if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'): + cfg['security'] = sxp.child_value(sxp_cfg, 'security') + elif not cfg.get('security_label'): + cfg['security'] = [['access_control', + ['policy', security.get_active_policy_name() ], + ['label', ACM_LABEL_UNLABELED ]]] + + if 'security' in cfg and not cfg.get('security_label'): + secinfo = cfg['security'] # The xm command sends a list formatted like this: # [['access_control', ['policy', 'xm-test'],['label', 'red']], # ['ssidref', 196611]] @@ -664,12 +671,16 @@ class XendConfig(dict): policy = secinfo[idx][aidx][1] if secinfo[idx][aidx][0] == "label": label = secinfo[idx][aidx][1] - import xen.util.xsm.xsm as security cfg['security_label'] = \ security.set_security_label(policy, label) if not sxp.child_value(sxp_cfg, 'security_label'): del cfg['security'] + sec_lab = cfg['security_label'].split(":") + if len(sec_lab) != 3: + raise XendConfigError("Badly formatted security label: %s" + % cfg['security_label']) + old_state = sxp.child_value(sxp_cfg, 'state') if old_state: for i in range(len(CONFIG_OLD_DOM_STATES)): diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index eaee766cf0..4558a9cec6 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -2460,12 +2460,14 @@ class XendDomainInfo: self, label): return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) - #Check label against expected one. - old_label = self.get_security_label(xspol_old) - if old_label != old_seclab: - log.info("old_label != old_seclab: %s != %s" % - (old_label, old_seclab)) - return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) + #Check label against expected one. Can only do this + # if the policy hasn't changed underneath in the meantime + if xspol_old == None: + old_label = self.get_security_label() + if old_label != old_seclab: + log.info("old_label != old_seclab: %s != %s" % + (old_label, old_seclab)) + return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) # relabel domain in the hypervisor rc, errors = security.relabel_domains([[domid, new_ssidref]]) @@ -2477,6 +2479,7 @@ class XendDomainInfo: # HALTED, RUNNING or PAUSED if domid == 0: if xspol: + self.info['security_label'] = seclab ssidref = poladmin.set_domain0_bootlabel(xspol, label) else: return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0) @@ -2488,6 +2491,7 @@ class XendDomainInfo: return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) self.info['security_label'] = seclab + try: xen.xend.XendDomain.instance().managed_config_save(self) except: diff --git a/tools/python/xen/xend/XendXSPolicyAdmin.py b/tools/python/xen/xend/XendXSPolicyAdmin.py index 2ca9f30096..d762bc8c4a 100644 --- a/tools/python/xen/xend/XendXSPolicyAdmin.py +++ b/tools/python/xen/xend/XendXSPolicyAdmin.py @@ -99,9 +99,10 @@ class XSPolicyAdmin: # This is meant as an update to a currently loaded policy if flags & xsconstants.XS_INST_LOAD == 0: raise SecurityError(-xsconstants.XSERR_POLICY_LOADED) + if flags & xsconstants.XS_INST_BOOT == 0: + self.rm_bootpolicy() rc, errors = loadedpol.update(xmltext) if rc == 0: - self.rm_bootpolicy() irc = self.activate_xspolicy(loadedpol, flags) # policy is loaded; if setting the boot flag fails it's ok. return (loadedpol, rc, errors) @@ -279,8 +280,7 @@ class XSPolicyAdmin: return None def get_hv_loaded_policy_name(self): - security.refresh_security_policy() - return security.active_policy + return security.get_active_policy_name() def get_policy_by_name(self, name): for pol in self.xsobjs.values(): @@ -300,8 +300,10 @@ class XSPolicyAdmin: return title def set_domain0_bootlabel(self, xspol, label): - """ Set the domain-0 bootlabel under the given policy """ - return xspol.set_vm_bootlabel(label) + """ Set the domain-0 bootlabel under the given policy. If the + current policy is the default policy, it will remove it. """ + rm_entry = (xspol.get_name() == "DEFAULT") + return xspol.set_vm_bootlabel(label, rm_entry) def rm_domain0_bootlabel(self): """ Remove the domain-0 bootlabel from the default boot title """ diff --git a/tools/python/xen/xend/server/XMLRPCServer.py b/tools/python/xen/xend/server/XMLRPCServer.py index 91eb21632d..6aad3df9bd 100644 --- a/tools/python/xen/xend/server/XMLRPCServer.py +++ b/tools/python/xen/xend/server/XMLRPCServer.py @@ -207,6 +207,12 @@ class XMLRPCServer: self.server.register_function(domain_create, 'xend.domain.create') self.server.register_function(domain_restore, 'xend.domain.restore') + # A couple of the security functions + from xen.util.xsm import xsm as security + for name in security.xmlrpc_exports: + fn = getattr(security, name) + self.server.register_function(fn, "xend.security.%s" % name) + self.server.register_introspection_functions() self.ready = True diff --git a/tools/python/xen/xm/activatepolicy.py b/tools/python/xen/xm/activatepolicy.py deleted file mode 100644 index 270b510eac..0000000000 --- a/tools/python/xen/xm/activatepolicy.py +++ /dev/null @@ -1,111 +0,0 @@ -#============================================================================ -# This library is free software; you can redistribute it and/or -# modify it under the terms of version 2.1 of the GNU Lesser General Public -# License as published by the Free Software Foundation. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -#============================================================================ -# Copyright (C) 2007 International Business Machines Corp. -# Author: Stefan Berger -#============================================================================ - -"""Activate the managed policy of the system. -""" - -import sys -from xen.util import xsconstants -from xml.dom import minidom -from xen.xm.opts import OptionError -from xen.xm import getpolicy, setpolicy -from xen.xm import main as xm_main -from xen.xm.main import server - -def help(): - return """ - Usage: xm activatepolicy [options] - - Activate the xend-managed policy. - - The following options are defined: - --load Load the policy into the hypervisor. - --boot Have the system boot with the policy. Changes the default - title in grub.conf. - --noboot Remove the policy from the default entry in grub.conf. - --remove Attempt to remove the current policy by installing the - default policy; this works only if no domains are - running. - """ - -def activate_policy(flags): - policystate = server.xenapi.XSPolicy.get_xspolicy() - xs_ref = policystate['xs_ref'] - if int(policystate['type']) == 0 or xs_ref == "": - print "No policy is installed." - return - rc = int(server.xenapi.XSPolicy.activate_xspolicy(xs_ref, flags)) - if rc == flags: - print "Successfully activated the policy." - else: - print "An error occurred trying to activate the policy: %s" % \ - xsconstants.xserr2string(rc) - -def remove_bootpolicy(): - server.xenapi.XSPolicy.rm_xsbootpolicy() - -def install_default_policy(): - if xm_main.serverType != xm_main.SERVER_XEN_API: - raise OptionError('xm needs to be configured to use the xen-api.') - xs_type = int(server.xenapi.XSPolicy.get_xstype()) - if xs_type & xsconstants.XS_POLICY_ACM == 0: - raise OptionError('ACM policy type not supported on system.') - policystate = server.xenapi.XSPolicy.get_xspolicy() - if int(policystate['type']) == 0: - print 'No policy is installed.' - return - if int(policystate['type']) != xsconstants.XS_POLICY_ACM: - print "Unknown policy type '%s'." % policystate['type'] - flags = int(policystate['flags']) - if flags & xsconstants.XS_INST_LOAD == 0: - print "Default policy is already loaded." - return - setpolicy.setpolicy(xsconstants.ACM_POLICY_ID, 'default', flags, True, - False) - -def main(argv): - if xm_main.serverType != xm_main.SERVER_XEN_API: - raise OptionError('xm needs to be configured to use the xen-api.') - flags = 0 - c = 1 - - while c < len(argv): - if '--boot' == argv[c]: - flags |= xsconstants.XS_INST_BOOT - elif '--load' == argv[c]: - flags |= xsconstants.XS_INST_LOAD - elif '--noboot' == argv[c]: - remove_bootpolicy() - elif '--remove' == argv[c]: - install_default_policy() - return - else: - raise OptionError("Unknown command line option '%s'" % argv[c]) - c += 1 - - if flags != 0: - activate_policy(flags) - - getpolicy.getpolicy(False) - -if __name__ == '__main__': - try: - main(sys.argv) - except Exception, e: - sys.stderr.write('Error: %s\n' % str(e)) - sys.exit(-1) diff --git a/tools/python/xen/xm/addlabel.py b/tools/python/xen/xm/addlabel.py index 0ba3b925ff..fcc3516a51 100644 --- a/tools/python/xen/xm/addlabel.py +++ b/tools/python/xen/xm/addlabel.py @@ -22,7 +22,6 @@ import os import sys -from xen.util import dictio import xen.util.xsm.xsm as security from xen.xm.opts import OptionError from xen.util import xsconstants @@ -37,14 +36,12 @@ def help(): xm addlabel